<?php

namespace Juheip\IpService\platforms\jingyun;

use Juheip\IpService\contract\AbstractPlatform;
use Juheip\IpService\exception\AuthException;
use think\facade\Cache;

class JingYunPlatform extends AbstractPlatform
{
    private $config = [];
    private $token = '';
    private $cache_time = 60 * 60 * 24;

    public function init(array $config): self
    {
        $this->config = $config;
        if (isset($this->config ['cache_time'])) {
            $this->cache_time = $this->config ['cache_time'];
        }
        $this->login();

        return $this;
    }

    private function login()
    {
        if (!Cache::get($this->cacheName())) {
            $response = $this->client
                //->setRequestHeader(['Content-Type' => 'application/json; chatset=utf-8'])
                ->request(
                    'post',
                    $this->config ['server_url'] . '/v2/login',
                    [
                        'username' => $this->config ['username'],
                        'password' => $this->config ['password']
                    ]
                );
            
            if ($response ['code'] != 201) {
                throw new AuthException($response ['message'], 400);
            }

            if (!isset($response ['data']['user']['token'])) {
                throw new AuthException('token 获取失败', 400);
            }

            Cache::set($this->cacheName(), $response ['data']['user']['token'], $this->cache_time);
            Cache::set($this->cacheUserIdName(), $response ['data']['user']['id'], $this->cache_time);
        }

        $this->token = Cache::get($this->cacheName());
    }

    private function cacheName()
    {
        return sprintf('%s:auth:token', self::class);
    }

    private function cacheUserIdName()
    {
        return sprintf('%s:user_id:cache', 'jingyun');
    }

    public function getUserId()
    {
        return Cache::get($this->cacheUserIdName());
    }

    /**
     * 搜索游戏
     * 该接口用于搜索游戏，支持通过关键词查询游戏信息。支持按游戏名称首字母、拼音首字母和完整名称进行模糊搜索。
     * @param string $key 搜索关键词
     */
    public function gameSearch($key)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/v2/game/search', ['key' => $key], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 使用首字母获取游戏
     * @param string $initial 可选首字母过滤
     */
    public function gameList($initial = '')
    {
        return $this->client->request('get', $this->config ['server_url'] . '/v2/game/list', ['initial' => $initial], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获取家庭A区可用节点,家庭节点按游戏查询接口
     * 接口用于统计指定游戏尚未占用的家庭宽带节点数量，结果按城市聚合并拆分主要运营商，便于运营评估可分配资源。
     * @param int $game_id
     */
    public function boxNodesByGame($game_id)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/box/nodes_by_game', ['game_id' => $game_id], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 创建家庭 A 区账户
     * 家庭 A 区账户创建接口限流: 默认每 1 秒最多 10 次请求（超限返回 429 Too Many Requests）
     * @param int $game_id 关联的游戏 ID
     * @param int $user_id 目标账户所属用户 ID
     * @param int $nodes_count 计划开通的节点总数，必须与所有 orders[].node_count 之和完全匹配。
     * @param array $orders [['city_id' => 310100, 'node_count' => 2, 'isp' => 'telecom']]
     * @param int $orders[]['city_id'] 城市 ID，对应家庭节点所在城市
     * @param int $orders[]['node_count'] 该城市期望开通的节点数量
     * @param string $orders[]['isp'] 节点运营商，允许值：telecom、unicom、mobile、education、tie、greatwall、unknown
     * @param int $months 默认1 购买时长（月），支持字符串输入，自动转换为数字；默认 1
     * @param string $username 指定账户用户名，需与 password 同时提供；系统会检查是否使用保留账号 b389 以及用户名是否已存在
     * @param string $password 指定账户密码，与 username 一起提交。若二者缺省，系统会随机生成账号信息
     * @param bool $test false 是否开通测试账户，默认 false
     */
    public function boxCreate($game_id, $user_id, $nodes_count, $orders, $months = 1, $username = '', $password = '', $test = false)
    {
        $params = [
            'game_id' => $game_id,
            'user_id' => $user_id,
            'nodes_count' => $nodes_count,
            'orders' => $orders,
            'months' => $months,
            'username' => $username,
            'password' => $password,
            'test' => $test
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/box/create', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 随机账户名密码
     */
    public function accountRandom()
    {
        return $this->client->request('get', $this->config ['server_url'] . '/v2/account/random', [], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获得用户下所有账户
     * 获取当前用户下的所有账户列表信息
     */  
    public function accountList()
    {
        return $this->client->request('get', $this->config ['server_url'] . '/v2/account/list', [], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 切换账户节点,为指定账户切换节点的网络运营商
     * @param int $account_id 账户 ID
     * @param array $toggles 切换配置数组
     * @param int $toggles ['node_id'] 节点 ID
     * @param int $toggles ['toggle_id'] 获取对应类型的可用节点，家庭 A 区，家庭 B 区为对应的城市 id。数据中心账户为 group_id
     * @param string $toggles ['toggle_isp'] 运营商选择,"random"表示随机切换,可选字段为 ‘telecom’, 'unicom', 'mobile',对应获取可用节点时的 'tel','uni'
     */
    public function toggleNode($account_id, $toggles)
    {
        $params = [
            'account_id' => $account_id,
            'toggles' => $toggles
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/toggle/node', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 导出账户节点信息,获取账户信息接口
     * @param int $id 账户 ID
     */
    public function accountInfo($id)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/v2/account/info', ['id' => $id], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获取账户信息,获取账户节点信息接口
     * @param int $id 账户 ID
     */
    public function accountNodes($id)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/v2/account/nodes', ['id' => $id], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获取家庭B区可用节点,获取游戏可用节点接口
     * @param int $game_id 游戏 ID
     */
    public function edgeEdgesByGame($game_id)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/edge/edges_by_game', ['game_id' => $game_id], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 创建家庭 B 区账户，家庭 B 区账户创建接口
     * 接口用于为当前登录用户开通家庭 B 区 账户。控制器整体启用 身份校验，并对请求体应用自动转换数字字段及执行类验证。
     * @param int $game_id 关联的游戏 ID
     * @param int $user_id 目标账户所属用户 ID
     * @param int $nodes_count 计划开通的节点总数，必须与所有 orders[].node_count 之和完全匹配。
     * @param array $orders [['city_id' => 310100, 'node_count' => 2, 'isp' => 'telecom']]
     * @param int $orders[]['city_id'] 城市 ID，对应家庭节点所在城市
     * @param int $orders[]['node_count'] 该城市期望开通的节点数量
     * @param string $orders[]['isp'] 节点运营商，允许值：telecom、unicom、mobile、education、tie、greatwall、unknown
     * @param int $months 默认1 购买时长（月），支持字符串输入，自动转换为数字；默认 1
     * @param string $username 指定账户用户名，需与 password 同时提供；系统会检查是否使用保留账号 b389 以及用户名是否已存在
     * @param string $password 指定账户密码，与 username 一起提交。若二者缺省，系统会随机生成账号信息
     * @param bool $test false 是否开通测试账户，默认 false
     */
    public function edgeCreate($game_id, $user_id, $nodes_count, $orders, $months = 1, $username = '', $password = '', $test = false)
    {
        $params = [
            'game_id' => $game_id,
            'user_id' => $user_id,
            'nodes_count' => $nodes_count,
            'orders' => $orders,
            'months' => $months,
            'username' => $username,
            'password' => $password,
            'test' => $test
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/edge/create', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获取定制线路城市列表[定制A区]
     */
    public function customCity()
    {
        return $this->client->request('get', $this->config ['server_url'] . '/custom/city', [], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 开通定制线路[定制A区]
     * @param array $orders 
     * @param array $orders.city_id 开通城市id,通过/custom/city获取
     * @param array $orders.count 开通数量
     * @param array $orders.isp 默认“随机” 运营商 "随机" | "电信" | "联通" | "移动" | "其他", 为保证开通成功，请尽量选择随机。
     * @param string $username 字母或数字,至少 4 位
     * @param string $password 字母或数字,至少 4 位
     * @param string $months 开通月份,默认为 1
     */
    public function customCreate($orders, $username = '', $password = '', $months = 1)
    {
        $params = [
            'orders' => $orders,
            'username' => $username,
            'password' => $password,
            'months' => $months
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/custom/create', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 更新定制线路[定制A区]
     * @param string $account_id 账户id
     * @param string $link_id 线路id
     * @param string $update_city_id 更新城市 id
     * @param string $update_isp 默认“随机” 运营商 "随机" | "电信" | "联通" | "移动" | "其他"
     */
    public function customLinkUpdate($account_id, $link_id = '', $update_city_id = '', $update_isp = '随机')
    {
        $params = [
            'account_id' => $account_id,
            'link_id' => $link_id,
            'update_city_id' => $update_city_id,
            'update_isp' => $update_isp
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/custom/link/update', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 搜索定制城市列表[定制A区]
     * @param string $keyword 省,城市,citycode,provinceCode
     */
    public function customCitySearch($keyword)
    {
        $params = [
            'keyword' => $keyword
        ];

        return $this->client->request('get', $this->config ['server_url'] . '/custom/city/search', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 定制B-获取可用地区[定制B区]
     * @param string $custom_tag 51 可选标签，用于区分不同渠道/API 用户的节点池；提交时会自动去除首尾空白。
     */
    public function edgeCustomStats($custom_tag = 51)
    {
        $params = [
            'custom_tag' => $custom_tag
        ];

        return $this->client->request('get', $this->config ['server_url'] . '/edge/custom/stats', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 定制B - 开通账户[定制B区]
     * @param array $orders 
     * @param array $orders.city_id 开通城市id,通过/custom/city获取
     * @param array $orders.count 开通数量
     * @param array $orders.isp 默认“随机” 运营商 "随机" | "电信" | "联通" | "移动" | "其他", 为保证开通成功，请尽量选择随机。
     * @param string $username 字母或数字,至少 4 位
     * @param string $password 字母或数字,至少 4 位
     * @param string $months 开通月份,默认为 1
     * @param string $custom_tag 可选标签，用于区分不同渠道/API 用户的节点池；提交时会自动去除首尾空白。
     */
    public function edgeCustomCreate($orders, $username = '', $password = '', $months = 1, $custom_tag = '')
    {
        $params = [
            'orders' => $orders,
            'username' => $username,
            'password' => $password,
            'months' => $months,
            'custom_tag' => $custom_tag
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/edge/custom/create', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 定制B-切换位置[定制B区]
     * @param string $account_id 账户id
     * @param string $link_id 线路id
     * @param string $city_id 更新城市 id
     * @param string $isp 默认“随机” 运营商 "随机" | "电信" | "联通" | "移动" | "其他"
     */
    public function edgeCustomUpdate($account_id, $link_id = '', $city_id = '', $isp = '随机')
    {
        $params = [
            'account_id' => $account_id,
            'link_id' => $link_id,
            'city_id' => $city_id,
            'isp' => $isp
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/edge/custom/update', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 定制B-续费[定制B区]
     * @param string $account_id 账户id
     * @param string $nodes 续费部分节点时不能为空
     * @param string $months 开通时间,默认为1月
     * @param string $type 续费全部还是部分节点
     */
    public function edgeCustomRenew($account_id, $nodes = '', $months = 1, $type = 'all')
    {
        $params = [
            'account_id' => $account_id,
            'nodes' => $nodes,
            'months' => $months,
            'type' => $type
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/edge/custom/renew', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 续费
     * @param int $id 续费的账户 ID，必须是当前用户或管理员可访问的账户。
     * @param int $month 续费月数，必须大于 0。未提供或无法解析时默认 1 个月
     * @param string $type 续费范围：all 续费账户下全部节点/连接；part 仅续费 nodes 中列出的目标。
     * @param string $nodes 使用英文逗号分隔的节点或自建连接 ID 列表，仅在 type = "part" 时必填。重复或无法解析的 ID 会自动去重
     */
    public function accountRenewAccount($id, $type, $nodes, $month = 1)
    {
        $params = [
            'id' => $id,
            'month' => $month,
            'type' => $type,
            'modes' => $nodes
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/account/renew_account', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 预处理Socks5 导出到 nodes
     * @param int $id 账户 ID,可通过获取用户下所有账户获取
     * @param string $text 需要预处理的节点,请使用我们导出的 socks5 格式
     */
    public function accountPrepareRenew($id, $text)
    {
        $params = [
            'id' => $id,
            'text' => $text
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/account/prepare_renew', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 根据账户 id批量获取账户
     * @param string $ids 账户 id 通过英文","分割。不属于该请求 token 用户的 id 会被过滤
     * @param string $expire 非必填 · 距离过期的天数,不是整数的会被格式化为整数
     */
    public function accountListIds($ids, $expire)
    {
        $params = [
            'ids' => $ids,
            'expire' => $expire
        ];

        return $this->client->request('get', $this->config ['server_url'] . '/account/list/ids', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获取数据中心可用节点
     * @param int $game_id
     */
    public function accountUsableNodes($game_id)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/v2/account/usable_nodes', ['game_id' => $game_id], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 创创建数据中心账户
     * @param int $game_id 关联的游戏 ID
     * @param int $user_id 目标账户所属用户 ID
     * @param int $nodes_count 计划开通的节点总数，必须与所有 orders[].node_count 之和完全匹配。
     * @param array $orders [['group_id' => 1, 'node_count' => 2]]
     * @param int $orders[]['group_id'] 开通地区，通过获取数据中心可用节点获取
     * @param int $orders[]['node_count'] 该城市期望开通的节点数量
     * @param int $months 默认1 购买时长（月），支持字符串输入，自动转换为数字；默认 1
     * @param string $username 指定账户用户名，需与 password 同时提供；系统会检查是否使用保留账号 b389 以及用户名是否已存在
     * @param string $password 指定账户密码，与 username 一起提交。若二者缺省，系统会随机生成账号信息
     * @param bool $test false 是否开通测试账户，默认 false
     */
    public function accountCreate($game_id, $user_id, $nodes_count, $orders, $months = 1, $username = '', $password = '', $test = false)
    {
        $params = [
            'game_id' => $game_id,
            'user_id' => $user_id,
            'nodes_count' => $nodes_count,
            'orders' => $orders,
            'months' => $months,
            'username' => $username,
            'password' => $password,
            'test' => $test
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/v2/account/create', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 批量导出用户下账户节点信息
     * @param string $ids 账户id 按","分割,可通过获得用户下所有账户获得。不属于请求 token 用户的 id 会被过滤
     * @param string $type "line" 或 "account", line 混合所有 account 输出, account 按 账户分割.默认为 account
     */
    public function batchExport($ids, $type)
    {
        $params = [
            'ids' => $ids,
            'type' => $type
        ];

        return $this->client->request('get', $this->config ['server_url'] . '/batch/export', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 使用游戏类型获取游戏
     * @param string $type 游戏类目，例如：手游
     */
    public function gameType($type)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/batch/export', ['type' => $type], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 格式化
     * @param string $text 需要处理的格式,支持多分割种格式,http,socks5都能识别
     */
    public function batchParse($text)
    {
        return $this->client->request('post', $this->config ['server_url'] . '/batch/export', ['text' => $text], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 批量切换节点位置
     * @param int $account_id 目标账户 ID
     * @param string $nodes 需要释放的节点或链路 ID 列表，使用英文逗号分隔，必须全部属于目标账户且不可重复。
     * @param array $items $items [['toggle_id' => 310100, 'toggle_isp' => 'telecom', 'count' => 2]]新节点的分配计划，按顺序消费 nodes 中的 ID
     * @param int $items[]['toggle_id'] 目标位置标识
     * @param string $items[]['toggle_isp'] 目标运营商，支持 telecom、unicom、mobile、random
     * @param int $items[]['count'] 本条计划需要的新节点/链路数量，所有 count 之和必须等于 nodes 数量
     */
    public function batchToggle($account_id, $nodes, $items)
    {
        $params = [
            'account_id' => $account_id,
            'nodes' => $nodes,
            'items' => $items
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/batch/toggle', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 批量切换多账户节点位置
     * @param int $account_id 目标账户 ID
     * @param string $nodes 需要释放的节点或链路 ID 列表，使用英文逗号分隔，必须全部属于目标账户且不可重复。
     * @param array $items $items [['account_id' => 2201, 'nodes' => '4101, 4102', 'items' => ['toggle' => 310100, 'toggle_isp' => 'telecome', 'count' => 1]]]新节点的分配计划，按顺序消费 nodes 中的 ID
     * @param int $items[]['item']['toggle_id'] 目标位置标识
     * @param string $items[]['item']['toggle_isp'] 目标运营商，支持 telecom、unicom、mobile、random
     * @param int $items[]['item']['count'] 本条计划需要的新节点/链路数量，所有 count 之和必须等于 nodes 数量
     */
    public function batchToggleAccounts($account_id, $nodes, $items)
    {
        $params = [
            'account_id' => $account_id,
            'nodes' => $nodes,
            'items' => $items
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/batch/toggle/accounts', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 批量续费
     * @param int $month 目标账户 ID
     * @param array $items $items [['account_id' => 2201, 'node_ids' => '4101, 4102']]新节点的分配计划，按顺序消费 nodes 中的 ID
     * @param int $items[]['account_id'] 目标账户 ID，必须属于当前登录用户（管理员除外）。
     * @param string $items[]['node_ids']  续费的节点或链路 ID，使用英文逗号分隔。对于数据中心/家庭/边缘账户填写节点 ID，定制账户填写链路 ID；列表不能为空
     */
    public function batchRenew($account_id, $items)
    {
        $params = [
            'account_id' => $account_id,
            'items' => $items
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/batch/renew', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 检查用户名
     * @param int $username 用户名
     */
    public function accountCheckUsername($username)
    {
        $params = [
            'username' => $username
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/account/check_username', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获取邀请的用户列表
     * @param int $username 用户名
     */
    public function userInviteList()
    {
        return $this->client->request('get', $this->config ['server_url'] . '/user/invite_list', [], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 转移用户给邀请的用户
     * @param int $account_id 目标账户 ID
     * @param int $user_id 目标账户所属用户 ID
     */
    public function accountTransferInvite($account_id, $user_id)
    {
        $params = [
            'account_id' => $account_id,
            'user_id' => $user_id
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/account/transfer/invite', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 获得长效api_key
     * @param int $account_id 目标账户 ID
     * @param int $user_id 目标账户所属用户 ID
     */
    public function userKeyGenerate($account_id, $user_id)
    {
        return $this->client->request('get', $this->config ['server_url'] . '/user/key/generate', [], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 刷新需使用Bearer token或API_KEY登录,刷新后原API_KEY失效
     */
    public function userKeyRefresh()
    {
        return $this->client->request('get', $this->config ['server_url'] . '/user/key/refresh', [], [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 开启L2tp
     * @param int $account_id 目标账户 ID
     */
    public function accountL2tpEnable($account_id)
    {
        $params = [
            'account_id' => $account_id
        ];

        return $this->client->request('post', $this->config ['server_url'] . '/account/l2tp/enable', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }

    /**
     * 下线账户l2tp/单点下线账户l2tp
     * @param int $account_id 目标账户 ID
     * @param int $target_id 家庭 B 为节点 id,定制 b 为link的 id
     */
    public function accountL2tpOfflineAll($account_id, $target_id = null)
    {
        $params = [
            'account_id' => $account_id
        ];

        if (!is_null($target_id)) {
            $params ['target_id'] = $target_id;
        }

        return $this->client->request('post', $this->config ['server_url'] . '/account/l2tp/offline/all', $params, [], ['Authorization' => 'Bearer ' . $this->token]);
    }
}